#include "spi0_flash.h"
#include "stdio.h"
void spi0_flash_init(void)
{
    spi0_init();
    uint8_t a[5] = {0xab, 0, 0, 0, 0};
    spi0_cs(0);
    spi0_write_and_read(a, NULL, sizeof(a));
    spi0_cs(1);

    uint8_t b[3] = {0xab, 0xff, 0xff};
    spi0_cs(0);
    spi0_write_and_read(b, NULL, sizeof(b));
    spi0_cs(1);
}

uint32_t spi0_flash_read_id(void)
{
    uint8_t a[3] = {0x9f, 0, 0};
    spi0_cs(0);
    spi0_write_and_read(a, NULL, 1);
    spi0_write_and_read(NULL, a, 3);
    spi0_cs(1);

    uint32_t id = (uint32_t)a[2];
    id |= (uint32_t)(a[1] << 8);
    id |= (uint32_t)(a[0] << 16);

    return id;
}

void spi0_flash_wait_readly(void)
{
    uint8_t state = 0;
    uint8_t command[] = {0x05};
    spi0_cs(0);
    spi0_write_and_read(command, NULL, 1);
    spi0_write_and_read(NULL, &state, 1);
    while (state & 0x01)
    {
        spi0_write_and_read(NULL, &state, 1);
    }
    spi0_cs(1);
}

void spi0_flash_write_enable()
{
    uint8_t command[] = {0x06, 0};
    spi0_cs(0);
    spi0_write_and_read(command, NULL, 1);
    spi0_cs(1);
}

uint8_t spi0_flash_erase_all(void)
{
    uint8_t command[] = {0x05, 0x06, 0xc7};
    spi0_flash_wait_readly();
    spi0_flash_write_enable();
    spi0_cs(1);
    spi0_write_and_read(&command[2], NULL, 1);
    spi0_cs(0);
    spi0_flash_wait_readly();
    return 1;
}

void spi0_flash_erase_sector(uint32_t addr)
{
    uint8_t command[] = {0x20, (uint8_t)(addr >> 16), (uint8_t)(addr >> 8), (uint8_t)(addr >> 0)};
    spi0_flash_wait_readly();
    spi0_flash_write_enable();
    spi0_cs(0);
    spi0_write_and_read(command, NULL, 4);
    spi0_cs(1);
    spi0_flash_wait_readly();
}

void spi0_flash_write_sector(uint16_t sector_num, uint8_t *write)
{
    uint32_t addr = sector_num * 4096;

    spi0_flash_erase_sector(addr);

    for (int i = 0; i < 16; i++)
    {
        spi0_flash_write(addr + i * 256, write + i * 256, 256);
    }
    spi0_flash_wait_readly();
}

void spi0_flash_read(uint32_t addr, uint8_t *read, uint32_t length)
{
    uint8_t command[] = {0x03, (uint8_t)(addr >> 16), (uint8_t)(addr >> 8), (uint8_t)(addr >> 0)};

    spi0_flash_wait_readly();
    spi0_cs(0);
    spi0_write_and_read(command, NULL, 4);
    spi0_write_and_read(NULL, read, length);
    spi0_cs(1);
}

/*写任意位置任意长度，写之前需要擦除*/
void spi0_flash_write(uint32_t addr, uint8_t *write, uint32_t length)
{
    /*一页256 bytes，超过256 bytes要分多次写入*/
    uint8_t command[] = {0x02, 0, 0, 0};
    uint32_t n;
    uint32_t s_addr = addr;

    uint32_t a_256_addr = addr; // 对齐到256 一页
    uint32_t b_256_addr = addr + length;

    uint32_t a_len = 0, b_len = 0; // 256前段和256后段

    if (s_addr % 256 != 0)
    {
        a_256_addr = ((s_addr + 256) / 256) * 256;

        if (a_256_addr > (s_addr + length)) // 结束地址小于a_256_addr  a_len=length
        {
            a_len = length;
        }
        else
        {
            a_len = a_256_addr - s_addr; // 结束地址大于等于a_256_addr  a_len=a_256_addr - addr
        }
    }

    /*
     * b_256_addr<a_256_addr  在一个page内
     * b_256_addr=a_256_addr  在两个page内
     * b_256_addr>a_256_addr  在三个及以上page内，中间有完整的page需要写入
     */
    if ((b_256_addr % 256) != 0)
    {
        b_256_addr = (b_256_addr / 256) * 256;

        if (b_256_addr >= a_256_addr) // 两个page以上 b_len 不等于0
        {
            b_len = s_addr + length - b_256_addr;
        }
    }

    /*只写入前段非256对齐的部分,256页对齐的用for写入*/
    if (a_len > 0)
    {
        s_addr = addr;
        command[1] = (uint8_t)(s_addr >> 16);
        command[2] = (uint8_t)(s_addr >> 8);
        command[3] = (uint8_t)(s_addr >> 0);
        spi0_flash_wait_readly();
        spi0_flash_write_enable();
        spi0_cs(0);
        spi0_write_and_read(command, NULL, 4);
        spi0_write_and_read(write, NULL, a_len);
        spi0_cs(1);
        spi0_flash_wait_readly();
    }

    /*写入中间整页地址*/
    n = (length - a_len - b_len) / 256;
    s_addr = a_256_addr;
    // printf("\r\n alen:%ld  blen:%ld blen_buf:%ld  a_addr:%lx  b_addr:%lx npages:%ld\r\n", a_len, b_len, length - b_len, a_256_addr, b_256_addr, n);
    for (int i = 0; i < n; i++)
    {
        command[1] = (uint8_t)(s_addr >> 16);
        command[2] = (uint8_t)(s_addr >> 8);
        command[3] = (uint8_t)(s_addr >> 0);
        spi0_flash_wait_readly();
        spi0_flash_write_enable();
        spi0_cs(0);

        spi0_write_and_read(command, NULL, 4);
        spi0_write_and_read(&write[i * 256 + a_len], NULL, 256);
        spi0_cs(1);
        spi0_flash_wait_readly();
        s_addr += 256;
    }

    if (b_len > 0)
    {
        s_addr = b_256_addr;
        command[1] = (uint8_t)(s_addr >> 16);
        command[2] = (uint8_t)(s_addr >> 8);
        command[3] = (uint8_t)(s_addr >> 0);
        spi0_flash_wait_readly();
        spi0_flash_write_enable();
        spi0_cs(0);
        spi0_write_and_read(command, NULL, 4);
        spi0_write_and_read(&write[length - b_len], NULL, b_len);
        spi0_cs(1);
        spi0_flash_wait_readly();
    }
    // printf("\r\n alen:%ld  blen:%ld  a_addr:%ld  b_addr:%ld\r\n", a_len, b_len, a_256_addr, b_256_addr);
}

/*删除任意位置，任意长度*/
void spi0_flash_erase(uint32_t addr, uint32_t length)
{
    uint32_t temp_addr = addr;

    uint32_t sector_nums = length / 4096;

    if ((addr % 4096 == 0) && (sector_nums % 4096 == 0) && (sector_nums > 0)) // 刚好整扇区,直接删除即可
    {
        /*删除所有扇区数据*/
        for (int i = 0; i < sector_nums; i++)
        {
            spi0_flash_erase_sector(temp_addr);
            temp_addr += 4096;
        }
    }
    else // 跨多个扇区 需要读取 前段和后端内容后写入
    {
        uint8_t s_read[4096]; // 前段
        uint8_t e_read[4096]; // 后段
        uint32_t s_addr;
        uint32_t e_addr;
        uint32_t s_len;
        uint32_t e_len;

        sector_nums++;//跨多个扇区 +1

        s_addr = (addr / 4096) * 4096;
        e_addr = addr + length;
        s_len = addr - s_addr;
        e_len = ((e_addr + 4096) / 4096) * 4096 - e_addr;

        // 读取
        spi0_flash_read(s_addr, s_read, s_len);
        spi0_flash_read(e_addr, e_read, e_len);
        // printf("\r\n*********************************** %ld\r\n", s_addr);
        // for (int i = 0; i < s_len; i++)
        // {
        //     printf("%02X ", s_read[i]);
        //     if ((i + 1) % 8 == 0)
        //         printf("\r\n");
        // }
        // printf("\r\n***********************************\r\n");
        /*删除所有扇区数据*/
        temp_addr = addr;
        for (int i = 0; i < sector_nums; i++)
        {
            spi0_flash_erase_sector(temp_addr);
            temp_addr += 4096;
        }
        // uint8_t temp[10];
        // spi0_flash_read(s_addr, temp, 10);
        // printf("\r\n axx:%02X %02X \r\n", temp[0], temp[9]);
        // 写入
        spi0_flash_write(s_addr, s_read, s_len);
        spi0_flash_write(e_addr, e_read, e_len);
    }
}